package com.example.web.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.example.web.config.JedisUtil;
import com.example.web.model.Address;
import com.example.web.model.User;

import lombok.extern.slf4j.Slf4j;
/**
 * 再使用缓存中，有许多疑问一直在我脑海中，就是下面的场景
 * 场景：
 * 	用户 收货地址
 * 	一对多关系
 * 	用户进入收获地址页面首先看到的是地址集合信息，点开详情才可以看到单个地址的信息	
 * 	那么现在，使用缓存，还要考虑数据的一致性。
 * 	疑问一：我是否应该再创建单个地址时就应该建立缓存？用什么数据类型呢？不是创建单个地址时缓存，哪有应该怎么处理？
 * 	疑问二：如果是创建单个地址时缓存，在展示地址集合时，我是否是要查询所有的单个地址信息，然后封装成list集合返回？
 * 	疑问三：如果不是疑问二，是先查表得到地址的list集合，放入缓存，然后每次先查缓存，没有查表，这样缓存的开销可能就是问题？数据的一致性又怎么保证？
 * 	
 * 	争对上面场景和疑问，我问过很多人，大家思路还是很广的，我自己也想了很多总结一下集中解决方案。
 * 	
 * 	
 * @author yzl
 *
 */
@Slf4j
@RestController
public class AddressController {

	/**
	 * 	方案一 ：使用hash
	 * 	1.)可以在创建单个信息的时候创建缓存，单个信息更加容易保证数据的一致性。使用的数据类型hash
	 * 		将用户的userId作为hash 的key值
	 * 		将地址的id作为hash 的mapkey值
	 * 		将单个地址信息作为 hash value
	 * 		结构大概是这样的Map<key,Map<mapkey,value>> = Map<userId,Map<id,address>> 
	 * 	2.）缓存的准确性：在不设置有效时间的情况下，缓存一致存在，数据的有效性很高。
	 * 	3.)缓存的开销： 这样的数据结构，在查询地址集合是可以直接通过userId来查询到一个list解合，查询单个地址又可以通过key和mapkey来确定单个地址信息，
	 * 		单挑数据较大，数量交多，缓存的开销会很大。浪费多。
	 * 	4.)缓存一致性：用户在争对单个地址的添加修改删除时，直接争对相应的缓存信息进行数据同步，这样也有效的解决了数据的一致性。
	 * 
	 * @param address
	 * @return
	 */
	@GetMapping("/save")
	public Map<String,Address> save(Address address) {
		
		//这里假设应用场景直接操作的缓存，正常场景是先存表再缓存，
		//存表过程中可以通过selectKey获取到自增id
		//所以这里直接用address.getId().toString()
		
		boolean bool = JedisUtil.setMapKey("user:id:"+address.getUserId(), address.getId().toString(), address);
		log.info("添加缓存:{}",bool);
		
		//获取单个hash值 这里得到的市Object 类型的 数据，不能直接强转成Address对象
		Object obj = JedisUtil.getMapKey("user:id:"+address.getUserId(), address.getId().toString());
		Address add1 = JSON.parseObject(JSON.parse(obj.toString()).toString(), Address.class);
		log.info("得到单个缓存值：{}",obj);
		log.info("得到单个缓存值：{}",add1);
		
		Address add2 = JedisUtil.getMapKeyEntity("user:id:"+address.getUserId(), address.getId().toString(), Address.class);
		log.info("得到单个缓存值：{}",add2);
	
		//获取userid下的所有地址的map集合
		Map<String,Address> map = JedisUtil.getMapEntityClass("user:id:"+address.getUserId(), Address.class);
		log.info("得到所有单个人的map集合:{}",map);
		JedisUtil.getMap("user:id:"+address.getUserId());
		
		//获取用户的地址集合
		Map<String,List<Address>> maplist = JedisUtil.getMapListEntityClass("user:id:"+address.getUserId(), Address.class);
		log.info("得到单个用户的地址集合：{}", maplist);
		log.info("得到单个用户的地址集合：{}", maplist.get("user:id:"+address.getUserId()));
		
		return map;	
	}
	/**
	 * 方案一 改进
	 * 	设置有效时间
	 * 	在添加和修改删除时像判断地址集合缓存是否存在，不存在查表后缓存集合，再缓存单个数据
	 * 	大大减少的缓存的开销
	 * @param userId
	 * @return
	 */
	@GetMapping("/getUserAddress")
	public Map<String, User> getUserAddress(Integer userId){
		User u =new User();
		u.setId(1);
		u.setName("小红");
		u.setAge("19");
		
		JedisUtil.setMapKey("user:userId:"+userId, userId.toString(), u);
		
		Map<String, User> map = JedisUtil.getMapEntityClass("user:userId:"+userId, User.class);
		log.info("得到所有单个人的map集合:{}",map);
		
		//获取用户的地址集合
		Map<String,List<Address>> maplist = JedisUtil.getMapListEntityClass("user:id:"+userId, Address.class);
		log.info("得到单个用户的地址集合：{}", maplist);
		log.info("得到单个用户的地址集合：{}", maplist.get("user:id:"+userId));
		map.get(userId.toString()).setAddress(maplist.get("user:id:"+userId));
		
		return map;
	}
	
	/**
	 * 方案二 使用list集合
	 * 	1.）在存储的过程中，单个地址不缓存，只在查询时缓存。
	 * 		将用户的userId 的key值
	 * 		地址集合作为 value
	 * 		数据格式 Map<key,list>
	 * 	2.)缓存准确性：不管是查询单个地址信息，还是地址集合信息，都是先查换错有没有，没有查表后缓存。这样缓存准确性得到提高。
	 * 	3.）缓存开销：不过缓存开销上，会存单个地址，也会存多个地址，争对单挑数据较小，数据量较少的情况更加好用
	 * 	4.）缓存数据的一致性：在单个地址的添加修改和删除之后，就需要干掉不同业务下与这条数据相关的所有缓存。缓存的一致性上说不是很理想。
	 * 	5.)为了弥补一致性问题上，就需要对这个条数据的相关的所有数据都需要设置缓存的有效时间。来减少缓存开销和提高数据的一致性
	 */
	@GetMapping("/get")
	public void save1() {
		List<User> users = new ArrayList<User>();
		User u = new User();
		u.setId(1);
		u.setName("小黄");
		u.setAge("19");
		users.add(u);
		JedisUtil.setObject("u:oneuser:"+1, u);
		User user = (User) JedisUtil.getObject("u:oneuser:"+1);
		log.info("查询获取单个缓存：{}",user);
		
		User u1 = new User();
		u1.setId(1);
		u1.setName("小红");
		u1.setAge("19");
		users.add(u);
		users.add(u1);
		JedisUtil.setList("u:user", users);
		List<User> us =  JedisUtil.getListEntity("u:user", User.class);
		log.info("查询获取多个缓存：{}",us);
		
		Address address = new Address();
		address.setId(3);
		address.setUserId(1);
		address.setAddress("北京");
		address.setDetails("asdad");
		JedisUtil.setMapKeyExpire("user:id:"+address.getUserId(), address.getId().toString(), address, 60);
	}
	
}
